package com.final_project.zombiemenace.model;

import java.util.Random;

import android.graphics.Matrix;

import com.final_project.zombiemenace.screens.GameScreenZombie;
import com.final_project.zombiemenace.util.ConstantsUtil;
import com.kilobolt.framework.Game;

public class Zombie {
	
	protected static final float FIX_ZOMBIE_CIRCLE_RADIUS = 1.5f;
	private static final int MAX_ZOMBIE_SPEED = 5;
	private float zombieSpeed = 0.5f;
	
	private static Random r = new Random();
	private int health;
	private int damage;
	private boolean isAlive;
	private boolean visible;
	private float centerX;
	private float centerY;
	private float speedX;
	private float speedY;
	protected static Player player = GameScreenZombie.getPlayer();
	public Circle zombieCircle = new Circle();
	public float zombieAngle;
	private Thread attack;

	public float getZombieSpeed() {
		return zombieSpeed;
	}

	public void setZombieSpeed(float zombieSpeed) {
		if (zombieSpeed <= MAX_ZOMBIE_SPEED) {
			this.zombieSpeed = zombieSpeed;
		}
	}
	/**
	 * Creates a new Zombie object by given health, damage, X coord, Y coord
	 * variables.
	 * 
	 * @param health
	 *            - integer variable representing the health of the zombie
	 * @param centerX
	 *            - integer variable representing the X coord of the zombie
	 * @param centerY
	 *            - integer variable representing the Y coord of the zombie
	 * @param damage
	 *            - integer variable representing the damage of the zombie
	 */
	public Zombie(Game game, int health, int damage, float speed) {
		setHealth(health);
		setCenterX(getRandomCoord(game));
		setCenterY(getRandomCoord(game));
		setDamage(damage);
		setAlive(true);
		setZombieSpeed(speed);
		visible = checkIfZombieIsVisible();
		initZombieAttackThread(damage);
	}

	private void initZombieAttackThread(final int damage) {
		attack = new Thread(new Runnable() {
			
			@Override
			public void run() {
				// if zombie is neat player and  zombie is alive then attack
				while(checkZombieToPlayerCollision()&& isAlive()){
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					Assets.zombieAttack.play(100f);
					int playersHealth = player.getHealth();
					player.setHealth(playersHealth - damage);
				}
			}
		});
	}

	/**
	 * Generates random coordinate between -300 and 300
	 * 
	 * @return integer number representing the generated random Coordinate
	 */
	private static int getRandomCoord(Game game) {
		int randomCoord = r.nextInt(600) - 300;
		if (randomCoord > 0 && randomCoord < game.getGraphics().getWidth()) {
			randomCoord += game.getGraphics().getWidth();
		}
		return randomCoord;
	}

	/**
	 * Updates the state of the zombie object
	 */
	public void update() {
		follow();
		zombieCircle.setCenterX(centerX);
		zombieCircle.setCenterY(centerY);
		zombieCircle.setRadius(Assets.greenZombie1.getWidth()
				/ FIX_ZOMBIE_CIRCLE_RADIUS);
		if (this.health <= 0) {
			this.isAlive = false;
		}
	}

	public boolean checkZombieToZombieCollision(Zombie z) {
		double distance = Math.sqrt(Math.pow(
				(z.zombieCircle.getCenterX() - this.zombieCircle.getCenterX()),
				2)
				+ Math.pow((z.zombieCircle.getCenterY() - this.zombieCircle
						.getCenterY()), 2));

		if (distance <= z.zombieCircle.getRadius()
				+ this.zombieCircle.getRadius()) {
			return true;
		}
		return false;
	}

	private boolean checkZombieToPlayerCollision() {
		float playerCircleCenterX = player.playerCircle.getCenterX();
		float playerCircleCenterY = player.playerCircle.getCenterY();
		float playerCircleRadius = player.playerCircle.getRadius();
		if (Math.pow((this.centerX - playerCircleCenterX), 2)
				+ Math.pow(this.centerY - playerCircleCenterY, 2) <= Math.pow(
				playerCircleRadius, 2)) {
			return true;
		}
		return false;
	}

	/**
	 * Move zombie towards the player coordinates
	 */
	public void follow() {
		if (checkZombieToPlayerCollision()) {
			speedX = 0;
			speedY = 0;
			attack();
			return;
		}else{
			if (attack.isAlive()) {
				attack.interrupt();
			}
		}
		if (this.getCenterX() < player.getCenterX()) {
			setSpeedX(zombieSpeed);
		} else if (this.getCenterX() > player.getCenterX()) {
			setSpeedX(-zombieSpeed);
		} else {
			setSpeedX(0);
		}

		if (this.getCenterY() < player.getCenterY()) {
			setSpeedY(zombieSpeed);
		} else if (this.getCenterY() > player.getCenterY()) {
			setSpeedY(-zombieSpeed);
		} else {
			setSpeedY(0);
		}
		visible = checkIfZombieIsVisible();
		move();
	}

	public Matrix rotateZombie(Zombie z, float zombieX, float zombieY) {

		calculateZombieAngle(z, zombieX, zombieY);
		// rotate image according to the degrees
		Matrix rotationImage = new Matrix();
		rotationImage.reset();
		rotationImage.postRotate(z.zombieAngle);
		return rotationImage;
	}

	public static void calculateZombieAngle(Zombie z, float zombieX,
			float zombieY) {
		// should be calculated for some time but when it reaches some borders
		// it should stop calculating it
		// calculate difference
		float deltaX = player.getCenterX() - zombieX;
		float deltaY = player.getCenterY() - zombieY;
		// calculate angle in degrees
		z.zombieAngle = (float) (Math.atan2(deltaY, deltaX) * (180 / Math.PI));
	}

	public void attack() {
		// attack player we can keep instance of player here and when in range
		// and call this method
		// should extract from player's health like so player.health - damage
		if(!attack.isAlive()){
			initZombieAttackThread(this.damage);
			attack.start();
		}
	}

	/**
	 * Moves the zombie with specific speed
	 */
	public void move() {
		centerX += speedX;
		centerY += speedY;
	}

	public int getHealth() {
		return health;
	}

	public void setHealth(int health) {
		this.health = health;
	}

	public int getDamage() {
		return damage;
	}

	public void setDamage(int damage) {
		this.damage = damage;
	}

	public boolean isAlive() {
		return isAlive;
	}

	public void setAlive(boolean isAlive) {
		this.isAlive = isAlive;
	}

	public boolean isVisible() {
		return visible;
	}

	private boolean checkIfZombieIsVisible() {
		if ((this.centerX < ConstantsUtil.MAX_SCREEN_WIDTH 
				&& this.centerX > ConstantsUtil.MIN_SCREEN_WIDTH)
				&& (this.centerY < ConstantsUtil.MAX_SCREEN_HEIGHT 
						&& this.centerY > ConstantsUtil.MIN_SCREEN_HEIGHT)) {
			return true;
		} else {
			return false;
		}
	}

	public void setVisible(boolean isVisible) {
		this.visible = isVisible;
	}

	public float getCenterX() {
		return centerX;
	}

	public void setCenterX(float centerX) {
		this.centerX = centerX;
	}

	public float getCenterY() {
		return centerY;
	}

	public void setCenterY(float centerY) {
		this.centerY = centerY;
	}

	public float getSpeedX() {
		return speedX;
	}

	public void setSpeedX(float speedX) {
		this.speedX = speedX;
	}

	public float getSpeedY() {
		return speedY;
	}

	public void setSpeedY(float speedY) {
		this.speedY = speedY;
	}

}
